package org.exhibition.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import jakarta.annotation.PostConstruct;
import org.common.util.JwtUtils;
import org.common.util.ThreadlocalUtils;
import org.exhibition.domain.entity.ExhibitionTicketEntity;
import org.exhibition.domain.pojo.exhibition.ExhibitionList;
import org.exhibition.domain.pojo.exhibition.ExhibitionTicket;
import org.exhibition.domain.pojo.exhibition.MyExhibitionOrder;
import org.exhibition.domain.pojo.exhibition.UserTicketOrder;
import org.exhibition.mapper.ExhibitionMapper;
import org.exhibition.service.ExhibitionOrderService;
import org.exhibition.service.ExhibitionService;
import org.museum.api.client.IdentityNameClient;
import org.museum.api.util.CommonResponse;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static org.exhibition.util.StaticVariable.Lock_Order_Key;


@Service
public class ExhibitionServiceImpl implements ExhibitionService {
    @Autowired
    private ExhibitionMapper exhibitionMapper;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ExhibitionOrderService executeOrderTransaction;
    @Autowired
    private IdentityNameClient identityNameClient;
    //获取一个阻塞队列
    private final BlockingQueue<UserTicketOrder> blockingQueue = new LinkedBlockingQueue<>();
    //获取一个单核线程池
    private final ThreadPoolExecutor executor = new ThreadPoolExecutor(1,1,1000, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>());
    //获取lua脚本
    private static final DefaultRedisScript<Long> Exhibition_Script;
    //获取点赞的lua脚本
    private static final DefaultRedisScript<Long> Like_Script;
    //获取取消点赞的lua脚本
    private static final DefaultRedisScript<Long> Cancel_Like_Script;
    //初始化lua脚本
    static {
        Exhibition_Script = new DefaultRedisScript<>();
        Exhibition_Script.setLocation(new ClassPathResource("exhibition.lua"));
        Exhibition_Script.setResultType(Long.class);
    }
    //初始化点赞的lua脚本
    static {
        Like_Script = new DefaultRedisScript<>();
        Like_Script.setLocation(new ClassPathResource("exhibitionLike.lua"));
        Like_Script.setResultType(Long.class);
    }
    //初始化取消点赞的lua脚本
    static {
        Cancel_Like_Script = new DefaultRedisScript<>();
        Cancel_Like_Script.setLocation(new ClassPathResource("cancelLike.lua"));
        Cancel_Like_Script.setResultType(Long.class);
    }

    //获取一个线程进行订单处理
    private class ExhibitionerThread implements Runnable{
        @Override
        public void run() {
            System.out.println("开始订单处理");
            while (true){
                System.out.println("线程" + Thread.currentThread().getName() + "正在处理订单");
                //从阻塞队列中取出订单
                UserTicketOrder userTicketOrder;
                try {
                    userTicketOrder = blockingQueue.take();
                    System.out.println("获取订单成功");
                    //获取锁,一个用户只允许下一单
                    RLock lock = redissonClient.getLock(Lock_Order_Key+userTicketOrder.getPhone());
                    //获取锁
                    if(!lock.tryLock()){
                        //获取锁失败
                        return;
                    }
                    try{
                        //判断该用户是否已购买
                        if(exhibitionMapper.getExhibitionTicketById(userTicketOrder.getTicketId(),userTicketOrder.getIdentity())>0){
                            System.out.println("该用户已购买");
                            return ;
                        }
                        // 3. 执行库存检查和下单（在同一个事务中）
                        System.out.println(executeOrderTransaction.executeOrderTransaction(userTicketOrder));
                    }finally {
                        //释放锁
                        lock.unlock();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    //初始化线程进行一直进行订单处理
    @PostConstruct
    public void init(){
        //初始化线程池
        executor.submit(new ExhibitionerThread());
    }


    @Override
    public List<ExhibitionList> getAllExhibition() {
        return exhibitionMapper.getAllExhibition();
    }

    @Override
    public ExhibitionList getExhibitionById(Integer id) {
        return exhibitionMapper.getExhibitionById(id);
    }

    @Override
    public List<Map<String, String>> getExhibitionByDate(LocalDate startDate) {
        List<String> exhibitionByDate =exhibitionMapper.getExhibitionByDate(startDate);
        return exhibitionByDate.stream().map(name -> Map.of("name", name)).toList();
    }

    @Override
    public List<ExhibitionTicket> getExhibitionByName(String name, LocalDate startDate) {
        System.out.println("1"+exhibitionMapper.getExhibitionByName(name,startDate));
        List<ExhibitionTicket> exhibitionTickets = new ArrayList<>();
        exhibitionTickets.add(exhibitionMapper.getExhibitionByName(name,startDate));
        return exhibitionTickets;
    }

    @Override
    public ExhibitionList getExhibitionDetailByName(String name, LocalDate startDate) {
        return exhibitionMapper.getExhibitionDetailByName(name,startDate);
    }

    @Override
    public String submitExhibitionReservation(UserTicketOrder userTicketOrder) throws JsonProcessingException {
        String phone= JwtUtils.getJwtUsername(ThreadlocalUtils.get());
        userTicketOrder.setPhone( phone);
        //执行lua脚本
        Long result = stringRedisTemplate.execute(Exhibition_Script,
                Collections.emptyList(),
                String.valueOf(userTicketOrder.getTicketId()),userTicketOrder.getIdentity());
        int r= 0;
        if (result != null) {
            r = result.intValue();
        }
        if(r!=0){
            if(r==1) return "展览已报名满";
            if(r==2) return "用户已购买";
        }
        //向阻塞队列存订单
        try {
            blockingQueue.put(userTicketOrder);
            System.out.println("订单已存入阻塞队列");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "预购成功";
    }

    @Override
    public boolean likeExhibition(Integer exhibitionId) {
        String phone= JwtUtils.getJwtUsername(ThreadlocalUtils.get());
        //执行点赞的lua脚本
        Long result = stringRedisTemplate.execute(Like_Script,
                Collections.emptyList(),
                phone, String.valueOf(exhibitionId));
        if (result != null) {
            return result == 0;
        }
        return false;
    }

    @Override
    public boolean isLikeExhibition(String phone, Integer exhibitionId) {
        System.out.println(Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember("exhibition:" + exhibitionId + ":liked_users", phone)));
        //判断用户是否点赞,使用判断是否是成员的方式
        return Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember("exhibition:" + exhibitionId + ":liked_users", phone));
    }

    @Override
    public boolean cancelLikeExhibition(Integer exhibitionId) {
        //取消点赞
        String phone= JwtUtils.getJwtUsername(ThreadlocalUtils.get());
        Long result = stringRedisTemplate.execute(Cancel_Like_Script,
                Collections.emptyList(),
                phone, String.valueOf(exhibitionId));
        if (result != null) {
            return result == 0;
        }
        return false;
    }

    @Override
    public String getExhibitionIdByName(String name) {
        //根据名称获取id
        String id=exhibitionMapper.getExhibitionIdByName( name);
        if(id!=null) return id;
        return "";
    }

    @Override
    public List<MyExhibitionOrder> getAllExhibitionReservation() {
        //获取用户id
        String phone= JwtUtils.getJwtUsername(ThreadlocalUtils.get());
        List<UserTicketOrder> reservation= exhibitionMapper.getAllExhibitionReservation(phone);
        List<MyExhibitionOrder> myExhibitionOrders=new ArrayList<>();
        System.out.println("订单"+reservation);
        for(UserTicketOrder u:reservation){
            MyExhibitionOrder myExhibitionOrder=new MyExhibitionOrder();
            //根据身份证获取用户名
            CommonResponse<String> relativeName = identityNameClient.getIdentityName(u.getIdentity());
            myExhibitionOrder.setUserName(relativeName.getData());
            //获取票的id
            Long ticket_id =exhibitionMapper.getExhibitionTicketIdById(u.getId());
            //获取展览的id
            ExhibitionTicketEntity exhibitionTicketEntity =exhibitionMapper.getExhibitionIdById(ticket_id.intValue());
            //根据展览id获取展览详情
            ExhibitionList exhibitionList =exhibitionMapper.getExhibitionById(exhibitionTicketEntity.getExhibitionId());
            //用胡图工具包把展览详情的对应属性赋值给myExhibitionOrder
            //用胡图工具包把展览详情的属性赋值给myExhibitionOrder
            BeanUtils.copyProperties(exhibitionList,myExhibitionOrder);
            myExhibitionOrder.setOrderId(u.getId().longValue());
            myExhibitionOrder.setTicketPrice(exhibitionTicketEntity.getPrice());
            /*myExhibitionOrder.setName(exhibitionList.getName());
            myExhibitionOrder.setStartTime(exhibitionList.getStartTime());
            myExhibitionOrder.setEndTime(exhibitionList.getEndTime());
            myExhibitionOrder.setCoverImage(exhibitionList.getCoverImage());
            myExhibitionOrder.setPrice(exhibitionList.getTicketPrice());
            myExhibitionOrder.setOrderId(u.getId().longValue());
            myExhibitionOrder.setCreateTime(u.getCreateTime());
            myExhibitionOrder.setStatus(u.getStatus());*/
            myExhibitionOrder.setCreateTime(u.getCreateTime());
            myExhibitionOrders.add(myExhibitionOrder);
        }
        return myExhibitionOrders;
    }

    @Override
    public Long getExhibitionTicketIdById(Integer id) {
        return exhibitionMapper.getExhibitionTicketIdById(id);
    }

    @Override
    public Integer getExhibitionIdById(Integer id) {
        return 1;
    }

}
